/*
 * Copyright (C) 2021 Baidu, Inc. All Rights Reserved.
 */
package com.chukun.rpc.protocol.handler;

import java.util.Map;

import org.springframework.cglib.reflect.FastClass;

import com.chukun.rpc.core.TinyRpcRequest;
import com.chukun.rpc.core.TinyRpcResponse;
import com.chukun.rpc.core.TinyRpcServiceHelper;
import com.chukun.rpc.protocol.protocol.MessageHeader;
import com.chukun.rpc.protocol.protocol.MessageStatus;
import com.chukun.rpc.protocol.protocol.MessageType;
import com.chukun.rpc.protocol.protocol.TinyRpcProtocol;

import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import lombok.extern.slf4j.Slf4j;

/**
 * <p>rpc框架请求处理器<p/>
 *
 * @author chukun
 * @version 1.0
 * @since 1.0-SNAPSHOT
 */
@Slf4j
public class TinyRpcRequestHandler extends SimpleChannelInboundHandler<TinyRpcProtocol<TinyRpcRequest>> {

    private final Map<String, Object> rpcServiceMap;

    public TinyRpcRequestHandler(Map<String, Object> rpcServiceMap) {
        this.rpcServiceMap = rpcServiceMap;
    }

    @Override
    protected void channelRead0(ChannelHandlerContext ctx,
                                TinyRpcProtocol<TinyRpcRequest> request) throws Exception {
        log.info("TinyRpcRequestHandler channelRead0 data arrival......");
        TinyRpcRequestProcessor.getInstance().submitRequest(() -> {
            TinyRpcProtocol<TinyRpcResponse> responseProtocol = new TinyRpcProtocol<>();
            TinyRpcResponse response = new TinyRpcResponse();
            MessageHeader header = request.getHeader();
            header.setMessageType((byte) MessageType.RESPONSE.getType());

            try{
                Object result = handleRequest(request.getBody());
                response.setData(result);

                header.setStatus((byte) MessageStatus.SUCCESS.getCode());
                responseProtocol.setHeader(header);
                responseProtocol.setBody(response);
            }catch (Throwable throwable) {
                header.setStatus((byte) MessageStatus.FAIL.getCode());
                response.setMessage(throwable.toString());
                log.error("Process request {} error", header.getRequestId(), throwable);
            }
            ctx.writeAndFlush(responseProtocol);
            log.info("TinyRpcRequestHandler channelRead0 data write success......");
        });
    }

    /**
     * 处理请求
     * @param request
     * @return
     * @throws Throwable
     */
    private Object handleRequest(TinyRpcRequest request) throws Throwable {
        String serviceKey = TinyRpcServiceHelper.buildServiceKey(request.getClassName(), request.getVersion());
        Object serviceBean = rpcServiceMap.get(serviceKey);
        if (serviceBean == null) {
            throw new RuntimeException(String.format("Service not exist: %s:%s", request.getClassName(),
                    request.getMethodName()));
        }
        Class<?> serviceClass = serviceBean.getClass();
        String methodName = request.getMethodName();
        Class<?>[] parameterTypes = request.getParameterTypes();
        Object[] parameters = request.getParams();

        FastClass fastClass = FastClass.create(serviceClass);
        int methodIndex = fastClass.getIndex(methodName, parameterTypes);
        return fastClass.invoke(methodIndex, serviceBean, parameters);
    }
}
